#include "iuRoute.hpp"
#include <cstdio>
#include "iuInterface.hpp"

/**
 * @brief Initialise la table de routage
 */
iuRoute::iuRoute()
{
   m_table = NULL;
}

/**
 * @brief Destructeur de la table de routage
 */
iuRoute::~iuRoute()
{
   iuRouteRule *current;

   while (m_table)
   {
      current = m_table;
      m_table = m_table->next;
      delete current;
   }
}

/**
 * @brief Rajoute une regle a la table de routage
 * @param newRule la nouvelle regle a ajouter
 */
void iuRoute::addRule(iuRouteRule *newRule)
{
   if ((m_table == NULL) || (newRule->mask.get() > m_table->mask.get()))
   {
      newRule->next = m_table;
      m_table = newRule;
   }
   else
   {
      iuRouteRule *prev = m_table;
      iuRouteRule *current = m_table->next;

      while (current && (newRule->mask.get() <= current->mask.get()))
      {
         prev = current;
         current = current->next;
      }

      newRule->next = current;
      prev->next = newRule;
   }
}

/**
 * @brief Enleve une regle a la table de routage
 * @param n Le numero dans la table de la regle a enlever
 */
void iuRoute::removeRule(int n)
{
   if (n == 1)
   {
      iuRouteRule *current = m_table;

      m_table = m_table->next;
      delete current;
   }
   else
   {
      iuRouteRule *prev, *current;
      int i;

      prev = m_table;
      for (current = m_table->next, i = 2; current && i < n; current = current->next, i++)
         prev = current;

      if (!current)
         printf("[Route-Table] Invalid rule number\n");
      else
      {
         prev->next = current->next;
         delete current;
      }
   }
}

/**
 * @brief Supprime toutes les regles de la table de routage qui referencent l'interface donnee
 * @param in l'interface a supprimer de la table de routage
 */
void iuRoute::removeRulesByInterface(const iuInterface *in)
{
   iuRouteRule *r, *prev, *next;

   prev = NULL;
   for (r = m_table; r; r = next)
   {
      next = r->next;
      if (r->interface == in)
      {
         if (prev)
            prev->next = r->next;
         else
            m_table = r->next;
         delete r;
      }
      else
         prev = r;
   }
}

/**
 * @brief Applique l'algorithme de routage a l'adresse passee en parametre
 * @param ip l'adresse que l'on veut resoudre
 */
iuRouteRule *iuRoute::route(const iuIPAddress &ip)
{
   for (iuRouteRule *p = m_table; p; p = p->next)
   {
      if (p->up && ((p->dest.get() & p->mask.get()) == (ip.get() & p->mask.get())))
         return p;
   }
   return NULL;
}

/**
 * @brief Active une regle de la table
 * @param n Le numero de la regle a activer
 */
void iuRoute::setUp(int n)
{
   iuRouteRule *p = m_table;

   for (int i = 1; p && i < n; i++)
      p = p->next;

   if (!p)
      printf("[Route-Table] Invalid rule number\n");
   else
      p->up = true;
}

/**
 * @brief Desactive une regle de la table
 * @param n Le numero de la regle a desactiver
 */
void iuRoute::setDown(int n)
{
   iuRouteRule *p = m_table;

   for (int i = 1; p && i < n; i++)
      p = p->next;

   if (!p)
      printf("[Route-Table] Invalid rule number\n");
   else
      p->up = false;
}

/**
 * @brief Affiche la table de routage
 */
void iuRoute::print() const
{
   int cpt;
   iuRouteRule *p;
   printf("Num  Destination     Gateway         Mask            Interface Flags\n");

   for (p = m_table, cpt = 1; p; p = p->next, cpt++)
   {
      int cpt2;

      //Affichage du numero de ligne
      cpt2 = printf("%d", cpt);
      for (int i = cpt2; i < 5; i++)
         printf(" ");

      //Affichage de l'adresse de destination
      cpt2 = printf("%s", p->dest.toString());
      for (int i = cpt2; i < 16; i++)
         printf(" ");

      //Affichage de l'adresse de passerelle
      cpt2 = printf("%s", p->gateway.toString());
      for (int i = cpt2; i < 16; i++)
         printf(" ");

      //Affichage du masque
      cpt2 = printf("%s", p->mask.toString());
      for (int i = cpt2; i < 16; i++)
         printf(" ");

      //Affichage de l'interface
      cpt2 = printf("eth%d", p->interface->getNumber());
      for (int i = cpt2; i < 10; i++)
         printf(" ");

      //Affichage des flags
      cpt2 = 0;
      if (p->up)
         cpt2 += printf("U");
      if (p->host)
         cpt2 += printf("H");
      if(p->gate)
         cpt2 += printf("G");
      printf("\n");
   }
}
